#pragma once

#include "compiler.hpp"
#include "runner.hpp"
#include "../comm/log.hpp"
#include "../comm/util.hpp"

#include <signal.h>
#include <unistd.h>
#include <jsoncpp/json/json.h>

namespace ns_compile_and_run
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_compiler;
    using namespace ns_runner;

    class CompileAndRun
    {
    public:
        // 检查用户的代码是否为空
        static bool isEmptyCode(std::string& code, int* status_code)
        {
            if( 0 == code.size() )
            {
                *status_code = -1;
                return false;
            }
            return true;
        }
        // 形成临时的源文件并将用户代码写入源文件
        static bool isMakeSrcfile(std::string& tmp_file_name, std::string& code, int* status_code)
        {
            if( !FileUtil::WriteFile(PathUtil::Src(tmp_file_name), code) )
            {
                // 这是一件丢脸的事情!_!
                *status_code = -2;
                return false;
            }

            return true;
        }
        // 编译用户代码
        static bool isCompile(std::string& tmp_file_name, int* status_code)
        {
            if( !Compiler::Compile(tmp_file_name) )
            {
                // 用户代码编译失败
                *status_code = -3;
                return false;
            }

            return true;
        }
        // 运行用户代码
        static bool isRun(std::string& tmp_file_name, int cpu_limit, int mem_limit, int* status_code)
        {
            int run_result_code = Runner::Run(tmp_file_name, cpu_limit, mem_limit);
            bool flag = true;
            if( run_result_code < 0 )
            {
                // 运行用户代码时，发生内部错误
                *status_code = -2;
                flag = false;
            }
            else if( run_result_code > 0 )
            {
                // 运行时程序运行崩溃
                *status_code = run_result_code;
                flag = false;
            }
            else if( 0 == run_result_code )
            {
                // 运行成功
                *status_code = 0;
            }

            return flag;
        }
    public:
        // 开始运行用户代码
        /*
        * 客户端发送过来的json串内容包括：
        * code：用户提交的代码
        * input：用户输入的数据（不考虑）
        * cpu_limit：时间要求
        * mem_limit：空间要求
        * 
        * 服务器返回的json串内容包括：
        * status：状态码
        * reason：请求结果
        * stdout：用户程序运行完的结果
        * stderr：优惠程序运行完的错误结果
        */
        static void Start(const std::string& in_json, std::string* out_json)
        {
            // 获取客户端传递过来的信息
            Json::Value in_value;
            Json::Reader reader;
            reader.parse(in_json, in_value);
            std::string code  = in_value["code"].asString();
            std::string input = in_value["input"].asString();
            int cpu_limit = in_value["cpu_limit"].asInt();
            int mem_limit = in_value["mem_limit"].asInt();

            // 记录程序运行的状态码
            int status_code = 0;
            
            // 形成唯一的文件名
            std::string tmp_file_name = FileUtil::UniqFileName();
            // 构建返回信息
            Json::Value out_value;

            // for test
            bool flag1 = true;
            bool flag2 = true;
            bool flag3 = true;
            bool flag4 = true;

            if( (flag1 = isEmptyCode(code, &status_code))                  &&
                (flag2 = isMakeSrcfile(tmp_file_name, code, &status_code)) &&
                (flag3 = isCompile(tmp_file_name, &status_code))           &&
                (flag4 = isRun(tmp_file_name, cpu_limit, mem_limit, &status_code)))
            {
                // 此时 0 == status_code，表示整个过程全部运行成功
                // 读取标准输出中的内容给用户返回
                if( 0 == status_code )
                {
                    std::string stdout_content;
                    FileUtil::ReadFile(PathUtil::Stdout(tmp_file_name), &stdout_content, true);
                    out_value["stdout"] = stdout_content;

                    // 读取标准错误中的内容给用户返回
                    std::string stderr_content;
                    FileUtil::ReadFile(PathUtil::Stderr(tmp_file_name), &stderr_content, true);
                    out_value["stderr"] = stderr_content;
                }
            }
            
            if(!flag1)
            {
                std::cout << "flag1 is false" << std::endl;
            }
            if(!flag2)
            {
                std::cout << "flag2 is false" << std::endl;
            }
            if(!flag3)
            {
                std::cout << "flag3 is false" << std::endl;
            }
            if(!flag4)
            {
                std::cout << "flag4 is false" << std::endl;
            }
            out_value["status"] = status_code;
            out_value["reason"] = CodeToDesc(status_code, tmp_file_name);
            

            Json::StyledWriter writer;
            *out_json = writer.write(out_value);

            // 清空临时文件
            RemoveTempFile(tmp_file_name);
        }
        
        // 返回错误码描述
        static std::string CodeToDesc(int code, const std::string& file_name)
        {
            std::string desc;

                 if(code ==  0)  { desc = "程序通过编译，运行结果如下：";   }
            else if(code == -1)  { desc = "提交的代码为空"; }
            else if(code == -2)  { desc = "未知错误";       }
            else if(code == -3)      { FileUtil::ReadFile(PathUtil::CompilerError(file_name), &desc, true);} // 代码编译时发生了错误
            else if(code == SIGABRT) { desc = "内存占用过大，请检查内存!";   }
            else if(code == SIGXCPU) { desc = "运行超时，请检查时间复杂度!"; }
            else if(code == SIGFPE)  { desc = "发生浮点数溢出错误";          }
            else if(code == SIGSEGV) { desc = "发生段错误";                 }
            else                     { desc = "未知："+std::to_string(code);}

            return desc;
        }

        // 清理临时文件
        static void RemoveTempFile(const std::string &file_name)
        {
            std::string _src = PathUtil::Src(file_name);
            if(FileUtil::IsFileExists(_src))
            {
                unlink(_src.c_str());
            }

            std::string _compiler_error = PathUtil::CompilerError(file_name);
            if(FileUtil::IsFileExists(_compiler_error))
            {
                unlink(_compiler_error.c_str());
            }

            std::string _execute = PathUtil::Exe(file_name);
            if(FileUtil::IsFileExists(_execute))
            {
                unlink(_execute.c_str());
            }

            std::string _stdin = PathUtil::Stdin(file_name);
            if(FileUtil::IsFileExists(_stdin))
            {
                unlink(_stdin.c_str());
            }

            std::string _stdout = PathUtil::Stdout(file_name);
            if(FileUtil::IsFileExists(_stdout))
            {
                unlink(_stdout.c_str());
            }

            std::string _stderr = PathUtil::Stderr(file_name);
            if(FileUtil::IsFileExists(_stderr))
            {
                unlink(_stderr.c_str());
            }
        }
    };
}